home *** CD-ROM | disk | FTP | other *** search
Text File | 1990-09-11 | 11.3 KB | 415 lines | [TEXT/MPS ] |
- /*------------------------------------------------------------------------------
- #
- # Apple Macintosh Developer Technical Support
- #
- # MultiFinder-Aware Simple TextEdit Sample Application
- #
- # CPlusTESample
- #
- # TESample.cp - C++ source
- #
- # Copyright © 1989 Apple Computer, Inc.
- # All rights reserved.
- #
- # Versions:
- # 1.10 07/89
- # 1.00 04/89
- #
- # Components:
- # CPlusTESample.make July 9, 1989
- # TApplicationCommon.h July 9, 1989
- # TApplication.h July 9, 1989
- # TDocument.h July 9, 1989
- # TECommon.h July 9, 1989
- # TESample.h July 9, 1989
- # TEDocument.h July 9, 1989
- # TApplication.cp July 9, 1989
- # TDocument.cp July 9, 1989
- # TESample.cp July 9, 1989
- # TEDocument.cp July 9, 1989
- # TESampleGlue.a July 9, 1989
- # TApplication.r July 9, 1989
- # TESample.r July 9, 1989
- #
- # CPlusTESample is an example application that demonstrates
- # how to initialize the commonly used toolbox managers,
- # operate successfully under MultiFinder, handle desk
- # accessories and create, grow, and zoom windows. The
- # fundamental TextEdit toolbox calls and TextEdit autoscroll
- # are demonstrated. It also shows how to create and maintain
- # scrollbar controls.
- #
- # This version of TESample has been substantially reworked in
- # C++ to show how a "typical" object oriented program could
- # be written. To this end, what was once a single source code
- # file has been restructured into a set of classes which
- # demonstrate the advantages of object-oriented programming.
- #
- ------------------------------------------------------------------------------*/
-
-
- /*
- Segmentation strategy:
-
- This program has only one segment, since the issues
- surrounding segmentation within a class's methods have
- not been investigated yet. We DO unload the data
- initialization segment at startup time, which frees up
- some memory
-
- SetPort strategy:
-
- Toolbox routines do not change the current port. In
- spite of this, in this program we use a strategy of
- calling SetPort whenever we want to draw or make calls
- which depend on the current port. This makes us less
- vulnerable to bugs in other software which might alter
- the current port (such as the bug (feature?) in many
- desk accessories which change the port on OpenDeskAcc).
- Hopefully, this also makes the routines from this
- program more self-contained, since they don't depend on
- the current port setting.
-
- Clipboard strategy:
-
- This program does not maintain a private scrap.
- Whenever a cut, copy, or paste occurs, we import/export
- from the public scrap to TextEdit's scrap right away,
- using the TEToScrap and TEFromScrap routines. If we did
- use a private scrap, the import/export would be in the
- activate/deactivate event and suspend/resume event
- routines.
- */
-
- #include <Types.h>
- #include <QuickDraw.h>
- #include <Fonts.h>
- #include <Events.h>
- #include <Controls.h>
- #include <Windows.h>
- #include <Menus.h>
- #include <TextEdit.h>
- #include <Dialogs.h>
- #include <Desk.h>
- #include <Scrap.h>
- #include <ToolUtils.h>
- #include <Memory.h>
- #include <SegLoad.h>
- #include <Files.h>
- #include <OSUtils.h>
- #include <Traps.h>
-
- // our class definitions
- #include "TEDocument.h"
- #include "TESample.h"
-
- // ExtremeNeg and ExtremePos are used to set up wide open rectangles and regions.
- const short kExtremeNeg = -32768;
- const short kExtremePos = 32767 - 1; // required to address an old region bug
-
- // kMaxOpenDocuments is used to determine whether a new document can be opened
- // or created. We keep track of the number of open documents, and disable the
- // menu items that create a new document when the maximum is reached. If the
- // number of documents falls below the maximum, the items are enabled again. */
- const short kMaxOpenDocuments = 1;
-
- // Define max and min macros for efficiency.
- #define max(a,b) ((a) > (b) ? (a) : (b))
- #define min(a,b) ((a) < (b) ? (a) : (b))
-
- // Our application object, initialized in main(). We make it
- // global so our functions which don't belong to any class
- // can find the active document.
- TESample *gTheApplication;
-
- // main is the entrypoint to the program
- int main(void)
- {
- // Create our application object. This MUST be the FIRST thing
- // done in main(), since it initializes the Toolbox for us.
- gTheApplication = new TESample;
- if (gTheApplication == nil) // if we couldn't allocate object (impossible!?)
- return 0; // go back to Finder
-
- // Start our main event loop running. This won't return until user quits
- gTheApplication->EventLoop();
-
- // We always return a value, like good little ANSI worshippers
- return 0;
- }
-
- // the constructor for our class, called automatically when we create
- // an instance of this class. In this particular case, we only want
- // one instance since the constructor does all the menu setups and
- // creates our (untitled) document.
- TESample::TESample(void)
- {
- Handle menuBar;
-
- // read menus into menu bar
- menuBar = GetNewMBar(rMenuBar);
- // install menus
- SetMenuBar(menuBar);
- DisposHandle(menuBar);
- // add DA names to Apple menu
- AddResMenu(GetMHandle(mApple), 'DRVR');
- DrawMenuBar();
-
- // create empty mouse region
- fMouseRgn = NewRgn();
- // create a single empty document
- DoNew();
- // make sure we have a valid cursor region
- AdjustCursor();
- }
-
- // Tell TApplication class how much heap we need
- long TESample::HeapNeeded(void)
- {
- return (kMinSize * 1024);
- }
-
- // Calculate a sleep value for WaitNextEvent. This takes into account the things
- // that DoIdle does with idle time.
-
- unsigned long TESample::SleepVal(void)
- {
- unsigned long sleep;
-
- sleep = kMaxSleepTime; // default value for sleep
- // if we aren't in background, let document tell us how long to sleep
- if ((!fInBackground) && (fCurDoc != nil))
- sleep = min(sleep,fCurDoc->CalcIdle());
- return sleep;
- }
-
- // This is called whenever we get a null event et al.
- // It takes care of necessary periodic actions. For this program,
- // it calls TEIdle.
-
- void TESample::DoIdle(void)
- {
- TEDocument* fTECurDoc = (TEDocument*) fCurDoc;
-
- if ( fTECurDoc != nil )
- fTECurDoc->DoIdle();
- } // DoIdle
-
- // Change the cursor's shape, depending on its position. This also calculates a
- // region that includes the cursor for WaitNextEvent.
-
- void TESample::AdjustCursor(void)
- {
- TEDocument* fTECurDoc = (TEDocument*) fCurDoc;
-
- // notice that we don't change cursor if front window isn't ours
- if ( (!fInBackground) && (fTECurDoc != nil) )
- {
- RgnHandle arrowRgn;
- RgnHandle iBeamRgn;
- Point mouse;
-
- // get mouse location and convert to global coordinates
- GetMouse(&mouse);
- LocalToGlobal(&mouse);
-
- // calculate regions for different cursor shapes
- arrowRgn = NewRgn();
- iBeamRgn = NewRgn();
- // start arrowRgn wide open
- SetRectRgn(arrowRgn, kExtremeNeg, kExtremeNeg, kExtremePos, kExtremePos);
- // calculate iBeamRgn
- fTECurDoc->GetVisTERgn(iBeamRgn);
- // subtract iBeamRgn from arrowRgn
- DiffRgn(arrowRgn, iBeamRgn, arrowRgn);
-
- // change the cursor and the region parameter
- if (PtInRgn(mouse, iBeamRgn))
- {
- SetCursor(*GetCursor(iBeamCursor));
- CopyRgn(iBeamRgn, fMouseRgn);
- }
- else
- {
- SetCursor(&qd.arrow);
- CopyRgn(arrowRgn, fMouseRgn);
- }
- // get rid of regions we don't need anymore
- DisposeRgn(arrowRgn);
- DisposeRgn(iBeamRgn);
- }
- } // AdjustCursor
-
- // Enable and disable menus based on the current state. The
- // user can only select enabled menu items. We set up all the
- // menu items before calling MenuSelect or MenuKey, since
- // these are the only times that a menu item can be selected.
- // Note that MenuSelect is also the only time the user will
- // see menu items. This approach to deciding what enable/
- // disable state a menu item has the advantage of
- // concentrating all the decision-making in one routine, as
- // opposed to being spread throughout the application. Other
- // application designs may take a different approach that may
- // or may not be as valid.
-
- void TESample::AdjustMenus(void)
- {
- WindowPtr frontmost;
- MenuHandle menu;
- long offset;
- Boolean undo;
- Boolean cutCopyClear;
- Boolean paste;
- TEDocument* fTECurDoc = (TEDocument*) fCurDoc;
-
- frontmost = FrontWindow();
-
- menu = GetMHandle(mFile);
- if ( fDocList->NumDocs() < kMaxOpenDocuments )
- EnableItem(menu, iNew); // New is enabled when we can open more documents
- else DisableItem(menu, iNew);
- if ( frontmost != (WindowPtr) nil ) // Close is enabled when there is a window to close
- EnableItem(menu, iClose);
- else DisableItem(menu, iClose);
-
- menu = GetMHandle(mEdit);
- undo = false;
- cutCopyClear = false;
- paste = false;
- if (frontmost != nil)
- {
- if (fTECurDoc == nil)
- {
- undo = true; // all editing is enabled for DA windows
- cutCopyClear = true;
- paste = true;
- }
- else
- {
- // Cut, Copy, and Clear is enabled for app. windows with selections
- if ( fTECurDoc->HaveSelection() )
- cutCopyClear = true;
- // If we have any TEXT in the scrap, enable paste
- if ( GetScrap(nil, 'TEXT', &offset) )
- paste = true;
- }
- }
- if ( undo )
- EnableItem(menu, iUndo);
- else DisableItem(menu, iUndo);
- if ( cutCopyClear )
- {
- EnableItem(menu, iCut);
- EnableItem(menu, iCopy);
- EnableItem(menu, iClear);
- }
- else
- {
- DisableItem(menu, iCut);
- DisableItem(menu, iCopy);
- DisableItem(menu, iClear);
- }
- if ( paste )
- EnableItem(menu, iPaste);
- else DisableItem(menu, iPaste);
- } // AdjustMenus
-
-
- // This is called when an item is chosen from the menu bar (after calling
- // MenuSelect or MenuKey). It does the right thing for each command.
-
- void TESample::DoMenuCommand(short menuID, short menuItem)
- {
- short itemHit;
- Str255 daName;
- short daRefNum;
- WindowPtr window;
- TEDocument* fTECurDoc = (TEDocument*) fCurDoc;
-
- window = FrontWindow();
- switch ( menuID )
- {
- case mApple:
- switch ( menuItem )
- {
- case iAbout: // bring up alert for About
- itemHit = Alert(rAboutAlert, nil);
- break;
- default: // all non-About items in this menu are DAs et al
- GetItem(GetMHandle(mApple), menuItem, daName);
- daRefNum = OpenDeskAcc(daName);
- break;
- }
- break;
- case mFile:
- switch ( menuItem )
- {
- case iNew:
- DoNew();
- break;
- case iClose:
- if (fTECurDoc != nil)
- {
- fDocList->RemoveDoc(fTECurDoc);
- fTECurDoc->DoClose();
- }
- else CloseDeskAcc(((WindowPeek) fWhichWindow)->windowKind);
- // make sure our current document/window references are valid
- fWhichWindow = FrontWindow();
- if (fWhichWindow != nil)
- {
- fCurDoc = fDocList->FindDoc(fWhichWindow);
- SetPort(fWhichWindow);
- }
- else fCurDoc = nil;
- break;
- case iQuit:
- Terminate();
- break;
- }
- break;
- case mEdit: // call SystemEdit for DA editing & MultiFinder
- if ( !SystemEdit(menuItem-1) )
- {
- switch ( menuItem )
- {
- case iCut:
- fTECurDoc->DoCut();
- break;
- case iCopy:
- fTECurDoc->DoCopy();
- break;
- case iPaste:
- fTECurDoc->DoPaste();
- break;
- case iClear:
- fTECurDoc->DoClear();
- break;
- }
- }
- break;
- }
- HiliteMenu(0); // unhighlight what MenuSelect (or MenuKey) hilited
- } // DoMenuCommand
-
- // Create a new document and window.
-
- void TESample::DoNew(void)
- {
- TEDocument* tDoc;
-
- tDoc = new TEDocument(rDocWindow);
- // if we didn't get an allocation error, add it to list
- if (tDoc != nil)
- fDocList->AddDoc(tDoc);
- } // DoNew
-
- // Clean up the application and exits. You might want to close all
- // of your documents (and ask the user to save them) here.
-
- void TESample::Terminate(void)
- {
- ExitLoop();
- } // Terminate
-
-